package com.learn.learndemo;

import org.junit.Test;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.commons.AdviceAdapter;

import java.io.FileInputStream;
import java.io.FileOutputStream;

public class ASMUnitTestAuto {

    @Test
    public void test(){
        try {
            FileInputStream fis = new FileInputStream("/Users/chendennies/Documents/022kotlin/LearnDemo/app/build/intermediates/javac/debugUnitTest/classes/com/learn/learndemo/InjectTest.class");
            ClassReader classReader = new ClassReader(fis);

            ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
            classReader.accept(new MyClassVisitor(Opcodes.ASM7, classWriter), ClassReader.EXPAND_FRAMES);

            byte[] bytes = classWriter.toByteArray();
            FileOutputStream fos = new FileOutputStream("/Users/chendennies/Documents/022kotlin/LearnDemo/app/build/intermediates/javac/debugUnitTest/classes/com/learn/learndemo/InjectTest3.class");
            fos.write(bytes);

            fis.close();
            fos.close();

//            InjectTest injectTest = new InjectTest();
//            injectTest.method();
        } catch (Exception E) {
            System.out.println("======" + E.toString());
        }


    }

    class MyClassVisitor extends ClassVisitor {
        public MyClassVisitor(int api, ClassVisitor classVisitor) {
            super(api, classVisitor);
        }

        @Override
        public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
            MethodVisitor methodVisitor = super.visitMethod(access, name, descriptor, signature, exceptions);
            System.out.println("method name=" + name + ",descriptor=" + descriptor + ",signature=" + signature);
            return new MyMethodVisitor(api, methodVisitor, access, name, descriptor);

        }
    }

    class MyMethodVisitor extends AdviceAdapter {
        protected MyMethodVisitor(int api, MethodVisitor methodVisitor, int access, String name, String descriptor) {
            super(api, methodVisitor, access, name, descriptor);
        }

        int start;
        int end;
        @Override
        protected void onMethodEnter() {
            super.onMethodEnter();
            if(!isNeedHotpot) {
                return;
            }
            mv.visitCode();
            Label label0 = new Label();
            mv.visitLabel(label0);
            mv.visitLineNumber(14, label0);
            mv.visitMethodInsn(INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J", false);
            mv.visitVarInsn(LSTORE, 1);

        }

        @Override
        protected void onMethodExit(int opcode) {
            super.onMethodExit(opcode);
            if(!isNeedHotpot) {
                return;
            }

            MethodVisitor methodVisitor = mv;
            Label label2 = new Label();
            methodVisitor.visitLabel(label2);
            methodVisitor.visitLineNumber(16, label2);
            methodVisitor.visitMethodInsn(INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J", false);
            methodVisitor.visitVarInsn(LSTORE, 3);
            Label label3 = new Label();
            methodVisitor.visitLabel(label3);
            methodVisitor.visitLineNumber(17, label3);
            methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
            methodVisitor.visitTypeInsn(NEW, "java/lang/StringBuilder");
            methodVisitor.visitInsn(DUP);
            methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "()V", false);
            methodVisitor.visitLdcInsn("executed time=");
            methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
            methodVisitor.visitVarInsn(LLOAD, 3);
            methodVisitor.visitVarInsn(LLOAD, 1);
            methodVisitor.visitInsn(LSUB);
            methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(J)Ljava/lang/StringBuilder;", false);
            methodVisitor.visitLdcInsn("ms");
            methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
            methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false);
            methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);

        }


        private boolean isNeedHotpot = false;
        @Override
        public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
            System.out.println(" visitAnnotation descriptor=" + descriptor);
            if("Lcom/learn/learndemo/ASMTime;".equals(descriptor)) {
                isNeedHotpot = true;
            }
            return super.visitAnnotation(descriptor, visible);
        }
    }
}
